package com.chenfan.process.biz.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.chenfan.ccp.common.exception.ApiException;
import com.chenfan.ccp.entity.BaseEntity;
import com.chenfan.ccp.util.sign.Md5Util;
import com.chenfan.ccp.util.start.ApplicationContextUtil;
import com.chenfan.ccp.util.tool.UuidUtil;
import com.chenfan.infra.constants.CommonBeanConstants;
import com.chenfan.infra.context.TenantContextHolder;
import com.chenfan.infra.context.UserContextHolder;
import com.chenfan.infra.vo.UserVO;
import com.chenfan.notify.dto.DingDTO;
import com.chenfan.notify.dto.NotifyDTO;
import com.chenfan.notify.vo.NotifyVo;
import com.chenfan.privilege.dto.UserDTO;
import com.chenfan.privilege.vo.DepartmentHeadVO;
import com.chenfan.privilege.vo.SUserVO;
import com.chenfan.process.biz.ProcessBizService;
import com.chenfan.process.common.constants.NumberCst;
import com.chenfan.process.common.constants.ProcessConstants;
import com.chenfan.process.common.exception.ProcessResultCode;
import com.chenfan.process.config.job.OverDueMessageRemindXXlJob;
import com.chenfan.process.context.NotifyEventContext;
import com.chenfan.process.dto.ApprovalCallBack;
import com.chenfan.process.dto.ApprovalDTO;
import com.chenfan.process.dto.ApprovalRejectedDTO;
import com.chenfan.process.entity.dto.ApprovalQueryDTO;
import com.chenfan.process.entity.dto.ApprovalTransferNodeDTO;
import com.chenfan.process.entity.dto.ApprovalUserDTO;
import com.chenfan.process.entity.dto.DetailAndProcessInstanceDTO;
import com.chenfan.process.entity.mo.CheckProcess;
import com.chenfan.process.entity.po.*;
import com.chenfan.process.entity.vo.StartApprovalVO;
import com.chenfan.process.enums.*;
import com.chenfan.process.event.callback.CallbackEvent;
import com.chenfan.process.event.callback.NotifyEvent;
import com.chenfan.process.pattern.template.AbstractNodeHandler;
import com.chenfan.process.service.ProcessTransferLogService;
import com.chenfan.process.service.TransferLogDetailService;
import com.chenfan.process.service.impl.NotifyDomainService;
import com.chenfan.process.util.*;
import com.chenfan.process.vo.ApprovalVo;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

import static com.chenfan.ccp.plug.business.constants.Constants.USER_ID;
import static com.chenfan.process.common.constants.Constant.REDIS_LOCK_TIME_SECOND;

/**
 * @author K
 * @date 2021/3/4
 */
@Slf4j
@Service
@RefreshScope
@Scope("prototype")
public class ProcessBizServiceImpl extends AbstractProcessBiz implements ProcessBizService {

    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    @Value("${overDueDay:7}")
    private Integer overDueDay;

    @Autowired
    private ProcessTransferLogService processTransferLogService;

    @Autowired
    private TransferLogDetailService transferLogDetailService;

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApprovalPo calculateNextOne(Long id, Long useId, ApprovalDTO approval) {
        if(Objects.isNull(id)){
            throw new ApiException(ProcessResultCode.APPROVAL_ID_CANNOT_EMPTY);
        }
        ApprovalDTO.Params param = approval.getParam();
        RedisLockUtil.lock(id.toString(), REDIS_LOCK_TIME_SECOND, TimeUnit.SECONDS);
        ApprovalPo approvalPo = getAndCheck(id);
        try {
            logger.info("收到审批:{},参数:{}", id, JSONObject.toJSONString(param));
            // 1. 拿到当前审批
            CheckProcess belong = getBelong(approvalPo, useId, id, param);

            ApprovalCallBack.ApprovalCallBackBuilder builder = ApprovalCallBack.builder().approvalFlag(Boolean.FALSE);
            // 查询是否有审批或者执行，再次查询一遍 目前主要是为了防止后续的数据修改，一致性，当前也可以直接做缓存或者CheckProcess传入信息
            ProcessDetailPo processDetailById = processDetailService.getById(belong.getId());

            if (!approval.isForced()) {
                permissionCheck(approvalPo);
            }
            if (!Boolean.TRUE.equals(param.isApprovalFlag())) {
                // 直接结束
                approvalDomainService.refuse(approvalPo, processDetailById, param.getRemark());
                return approvalPo;
            }

            ProcessPo nextProcess = belong.getNextProcess();

            // 循环处理
            if (Objects.nonNull(nextProcess)) {
                processDetailById.agree(param.getRemark());

                boolean skipped = Boolean.FALSE;
                while (true) {
                    // 设置是否相同
                    AbstractNodeHandler handler = (AbstractNodeHandler) ApplicationContextUtil.getContext().getBean("simpleNodeHandler");
                    List<ProcessRelatedPo> processRelatedByAll = approvalSnapshotPoService.getRelation(id, nextProcess.getId(), processDetailById);

                    if (processRelatedByAll.size() > 1) {
                        handler = (AbstractNodeHandler) ApplicationContextUtil.getContext().getBean("complexNodeHandler");
                    }
                    List<NotifyVo> notifyList = notifyBiz.listNotifyByApprovalId(Collections.singletonList(id));
                    if(ObjectUtil.isNotEmpty(notifyList)){
                        notifyBiz.dealWithNotify(notifyList, processDetailById);
                    } else {
                        log.info("审批待办消息没有，审批id为：id:{}",id);
                    }
                    DetailAndProcessInstanceDTO execute = handler.execute(approvalPo, processRelatedByAll, processDetailById, approval, approval.getParam().getParameter(), skipped, false);
                    if (Objects.isNull(execute)) {
                        break;
                    }
                    processDetailById = execute.getProcessDetail();
                    nextProcess.setId(processDetailById.getNextProcessId());
                    skipped = Boolean.TRUE;
                }
            } else {
                processDetailById.update();
                finishApproval(approvalPo, processDetailById, null, Boolean.TRUE, false);
                // 更新审批的状态
                eventPublisherBiz.publishEvent(builder, id, Boolean.TRUE, Boolean.TRUE, null, null,
                    EventTypeEnums.APPROVAL.getCode(), processDetailById.getRemark(),
                    processDetailById.getApprovalTemplateId(), processDetailById.getNextProcessId());
                notifyBiz.dealWithNotify(id);
            }
        } finally {
            RedisLockUtil.unlock(id.toString());
        }
        return approvalPo;
    }

    public void finishApproval(ApprovalPo approvalPo, ProcessDetailPo processDetail, String remark, boolean normal, boolean skipped) {
        ProcessInstanceApprovePo processInstanceApprovePo = processInstanceApproveService.getByApprovalId(approvalPo.getApprovalId());
        // 更新审批单状态
        if (!normal) {
            approvalPo.refuse();
            processInstanceApprovePo.refuse();
            processDetail.refuse();
        } else {
            // 表示正常结束
            approvalPo.finish();
            processInstanceApprovePo.pass();
            processDetail.pass();
        }
        if (!StringUtils.isEmpty(remark) && remark.length() > 500) {
            throw new ApiException(ProcessResultCode.OVER_MAX_COMMENT_SIZE);
        }
        processDetail.setRemark(remark);
        approvalService.updateById(approvalPo);

        if (skipped) {
            processDetail.setRemark("系统自动执行, 原因: 与历史节点审批人相同");
            processInstanceApprovePo.setRemark("系统自动执行, 原因: 与历史节点审批人相同");
        }
        processDetailService.updateById(processDetail);
        processInstanceApproveService.updateById(processInstanceApprovePo);

    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public StartApprovalVO startApproval(String projectName, String approvalTemplateCode, ApprovalDTO approval) {
        if (StringUtils.isEmpty(approval.getUseId())) {
            throw new ApiException(ProcessResultCode.APPROVAL_USER_NOT_FOUND);
        }

        // TODO 更改租户 + Code查询 找到模板信息
        ApprovalTemplatePo template = approvalTemplateService.getByCode(approvalTemplateCode);
//        String redisKey = String.format("%s_%s", template.getId(), approval.getBusinessId());

//        RedisLockUtil.lock(redisKey, 1, TimeUnit.MINUTES);
        if (Objects.isNull(template)) {
            throw new ApiException(ProcessResultCode.TEMPLATE_NOT_EXISTS);
        }

        // 模板已停用
        template.availableCheck();
        checkDuplicated(template, approval);

        ProcessRelatedPo processRelatedPo = processRelatedService.findHead(template.getId(), template.getVersion());
        if (Objects.isNull(processRelatedPo)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }

        List<ProcessRelatedPo> processRelatedPoList = processRelatedService.listProcessRelatedAfterProcessId(template.getId(), template.getVersion(), processRelatedPo.getProcessBeforeId());
        if (CollectionUtils.isEmpty(processRelatedPoList)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }

        // 计算.
        UserVO userVO = new UserVO();
        userVO.setUserId(approval.getUseId());
        userVO.setRealName(approval.getUserName());
        userVO.setTenantId(TenantUtil.getTenantId());
        UserContextHolder.setUserVo(userVO);

        // 执行人应该为当前节点的下一个节点的负责人
        ApprovalPo approvalPo = ApprovalPo.init(template.getId(), template.getVersion(), approval);
        approvalPo.setServiceName(projectName);

        ProcessDetailPo before = null;
        List<ProcessRelatedPo> calculateProcess = processRelatedPoList;

        // 如果没上级.
        boolean skip = Boolean.FALSE;
        while (true) {
            AbstractNodeHandler handler = (AbstractNodeHandler) ApplicationContextUtil.getContext().getBean("simpleNodeHandler");
            if (calculateProcess.size() > 1) {
                handler = (AbstractNodeHandler) ApplicationContextUtil.getContext().getBean("complexNodeHandler");
            }
            DetailAndProcessInstanceDTO execute = handler.execute(approvalPo, calculateProcess, before, approval, approval.getCheckField().getCheckFields(), skip, true);

            if (Objects.isNull(execute)) {
                break;
            }

            before = execute.getProcessDetail();
            Long nextProcessId = before.getNextProcessId();
            if (before.isSharedApproval()) {
                // 如果是或签的情况下, 说明出现了情况, 特殊处理一下.
                List<ProcessRelatedPo> processRelatedPos;
                if (Objects.nonNull(before.getGroupId())) {
                    processRelatedPos = processRelatedService.listWithGroup(template.getId(), template.getVersion(), before.getGroupId());
                } else {
                    processRelatedPos = processRelatedService.listProcessRelatedAfterProcessId(template.getId(), template.getVersion(), before.getProcessId());
                }
                // 随便取一个后继节点作为当前节点
                Long processAfterId = processRelatedPos.get(0).getProcessAfterId();
                nextProcessId = processAfterId;
            }
            calculateProcess = processRelatedService.listProcessRelatedAfterProcessId(template.getId(), template.getVersion(), nextProcessId);
            skip = Boolean.TRUE;
        }
        replace(approvalPo);
        approvalService.save(approvalPo);

        // 使用领域服务来进行处理
        approvalSnapshotPoService.save(approvalPo, template, template.getVersion());
        TemplateConfigPo config = templateConfigService.getByTemplateId(approvalPo.getApprovalTemplateId(), approvalPo.getVersion());

        if (Boolean.TRUE.equals(config.getRemindNotify())) {
            redisTemplate.opsForZSet().add(OverDueMessageRemindXXlJob.OVERDUE_MESSAGE, approvalPo.getApprovalId(), System.currentTimeMillis() + overDueDay * OverDueMessageRemindXXlJob.DAY);
        }

//        RedisLockUtil.unlock(redisKey);
        return StartApprovalVO.builder().approvalPo(approvalPo).processDetailPo(before).skipped(skip).finishFlag(approvalPo.isFinish()).build();

    }

    private void replace(ApprovalPo approvalPo) {
        List<ProcessInstanceApprovePo> processInstances = processInstanceApproveService.getApproval(approvalPo.getApprovalId());
        if (CollectionUtils.isEmpty(processInstances)) {
            return;
        }
        approvalPo.setUserId(processInstances.stream().map(e -> e.getUserId().toString()).collect(Collectors.joining(",")));
        approvalPo.setUserName(processInstances.stream().map(ProcessInstanceApprovePo::getUserName).collect(Collectors.joining(",")));
    }

    @Override
    public void sendNotify(StartApprovalVO startApprovalVO, ApprovalDTO app) {
        ApprovalPo approvalPo = startApprovalVO.getApprovalPo();
        ProcessDetailPo processDetailPo = startApprovalVO.getProcessDetailPo();
        boolean finish = startApprovalVO.getFinishFlag();
        if (finish) {
            // 发送数据
            ApprovalCallBack.ApprovalCallBackBuilder builder = ApprovalCallBack.builder().approvalFlag(Boolean.TRUE);
            builder.skipped(startApprovalVO.getSkipped());
            eventPublisherBiz.publishEvent(builder, approvalPo.getApprovalId(), Boolean.TRUE, Boolean.TRUE, "",
                approvalPo.getUserName(), EventTypeEnums.APPROVAL.getCode(), Objects.nonNull(processDetailPo) ? processDetailPo.getRemark() : null,
                approvalPo.getApprovalTemplateId(), Objects.nonNull(processDetailPo) ?  processDetailPo.getProcessId() : null);
            return;
        }
        Long approvalId = approvalPo.getApprovalId();
        List<ProcessInstanceApprovePo> approval = processInstanceApproveService.getApproval(approvalId);
        List<ApprovalUserDTO> approvalUsers = approval.stream()
            .map(e -> ApprovalUserDTO.builder().userId(e.getUserId()).userName(e.getUserName()).build())
            .collect(Collectors.toList());

        String userId = approvalUsers.stream().map(element -> element.getUserId().toString()).collect(Collectors.joining(","));
        String userName = approvalUsers.stream().map(ApprovalUserDTO::getUserName).collect(Collectors.joining(","));

        // 当前的ID
        List<ProcessDetailPo> processDetailPos = processDetailService.listProcessDetailByApprovalId(Collections.singletonList(approvalPo.getApprovalId()));

        List<Long> processIdList = processDetailPos.stream().map(ProcessDetailPo::getProcessId).collect(Collectors.toList());

        Optional<List<ProcessCarbonCopyPo>> processCarbonCopyPo = carbonCopyService.listByProcessId(approvalPo.getApprovalTemplateId(), processIdList);
        List<ProcessCarbonCopyPo> ccList = processCarbonCopyPo.orElse(Collections.emptyList());

        List<ApprovalCallBack.CarbonCopyDTO> dtoList = ccList.stream().distinct().map(ProcessCarbonCopyPo::toDTO).collect(Collectors.toList());

        // 处理全部或者创建人
        Optional<ProcessCarbonCopyPo> createOp = ccList.stream().filter(e -> Objects.equals(e.getType(), CarbonCopyEnum.APPROVAL_CREATE_PERSON.getCode())).findAny();
        if (createOp.isPresent()) {
            dtoList.add(ApprovalCallBack.CarbonCopyDTO.builder().userId(approvalPo.getCreateBy()).type(CarbonCopyEnum.NORMAL.getCode()).userName(approvalPo.getCreateName()).build());
        }

        // 所有人
        Optional<ApprovalCallBack.CarbonCopyDTO> any = dtoList.stream().filter(e -> Objects.equals(e.getType(), CarbonCopyEnum.ALL.getCode())).findAny();
        if (any.isPresent()) {
            // 找到所有的人然后去重
            List<ProcessInstanceApprovePo> processInstances = processInstanceApproveService.listApprovalInstance(Collections.singletonList(approvalId));
            List<ApprovalCallBack.CarbonCopyDTO> list = processInstances.stream().map(
                            e -> ApprovalCallBack.CarbonCopyDTO.builder().userId(e.getUserId()).userName(e.getUserName()).build())
                    .collect(Collectors.toList());
            dtoList.addAll(list);
        }

        dtoList = dtoList.stream().filter(ApprovalCallBack.CarbonCopyDTO::filter).distinct().collect(Collectors.toList());

        // 回调事件, 审批创建
        CallbackEvent callbackEvent = new CallbackEvent(
                ApprovalCallBack.builder().approvalFinished(Boolean.FALSE).approvalFlag(Boolean.TRUE)
                        .eventType(EventTypeEnums.CREATE.getCode()).nextApprovalUserId(userId).nextApprovalUserName(userName)
                        .approvalId(approvalPo.getApprovalId()).carbonCopyList(dtoList).skipped(startApprovalVO.getSkipped())
                        .start(Boolean.TRUE).userId(app.getUseId()).userName(app.getUserName()).build(),
                UserContextHolder.getToken());
        TransactionUtils.afterCommit(() -> publisher.publishEvent(callbackEvent));
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Long revokeApproval(Long approvalId, UserDTO userDTO) {
        RedisLockUtil.lock(approvalId.toString(), REDIS_LOCK_TIME_SECOND, TimeUnit.SECONDS);

        try {
            List<ProcessDetailPo> unProcessApproval = processDetailService.listUnProcessApproval(approvalId);

            if (CollectionUtils.isEmpty(unProcessApproval)) {
                throw new ApiException(ProcessResultCode.PRIVILEGE_NOT_FOUND);
            }

            if (Objects.nonNull(userDTO) && Objects.nonNull(userDTO.getUserId())) {
                UserVO userVO = new UserVO();
                // TODO 从上下文中取
                userVO.setUserId(userDTO.getUserId());
                userVO.setRealName(userDTO.getUserName());
                userVO.setTenantId(TenantUtil.getTenantId());
                UserContextHolder.setUserVo(userVO);
                log.info("撤回的租户ID:{}, 网关获取的用户信息:{}", TenantContextHolder.getTenantId(), USER_ID.get());
                if (Objects.nonNull(TenantContextHolder.getTenantId())) {
                    MDC.put(CommonBeanConstants.TENANT_ID, TenantContextHolder.getTenantId().toString());
                }
            }

            // 获取到Process Detail Id
            ProcessDetailPo processDetailPo = unProcessApproval.get(0);
            processDetailPo.revoke();

            // 需要将审批单更改为已撤回
            ApprovalPo approval = approvalService.findApprovalByApprovalId(approvalId);

            if (Objects.nonNull(approval)) {
                approval.revoke();
                approvalService.updateById(approval);
            }

            logger.info("撤回传入的用户信息:{}", userDTO);

            logger.info("撤回传入的用户信息:{}", userDTO);
            // TODO 排查问题
            notifyRemoteService.approvalFinish(approvalId);
            // 通知Notify系统关闭掉内容
            processDetailService.updateById(processDetailPo);
            // 设置为拒绝
            processInstanceApproveService.updateApproval(approvalId, InstanceEnums.REVOKE.getCode(), null);
        } finally {
            RedisLockUtil.unlock(approvalId.toString());
        }
        return 1L;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public ApprovalPo rejectApproval(Long approvalId, ApprovalRejectedDTO rejected) {
        if (!StringUtils.isEmpty(rejected.getRemark()) && rejected.getRemark().length() > 200) {
            throw new ApiException(ProcessResultCode.OVER_MAX_COMMENT_SIZE);
        }
        if (Objects.nonNull(rejected) && Objects.nonNull(rejected.getUserId())) {
            UserVO userVO = new UserVO();
            // TODO 从上下文中取
            userVO.setUserId(rejected.getUserId());
            userVO.setRealName(rejected.getUserName());
            userVO.setTenantId(TenantUtil.getTenantId());
            UserContextHolder.setUserVo(userVO);
            log.info("撤回的租户ID:{}, 网关获取的用户信息:{}", TenantContextHolder.getTenantId(), USER_ID.get());
            if (Objects.nonNull(TenantContextHolder.getTenantId())) {
                MDC.put(CommonBeanConstants.TENANT_ID, TenantContextHolder.getTenantId().toString());
            }
        }

        ApprovalPo approvalPo = getAndCheck(approvalId);
        // 校验后继节点的权限跟当前的操作人是否一致
        permissionCheck(approvalPo);

        // 查询当前节点.
        List<ProcessDetailPo> processList = processDetailService.listProcessDetailByApprovalId(Collections.singletonList(approvalId));
        ProcessDetailPo processDetailPo = validateRejected(approvalPo, processList);
        List<NotifyVo> notifyList = notifyBiz.listNotifyByApprovalId(Collections.singletonList(approvalId));
        updateStatusToRejected(approvalPo, rejected);
        afterReject(approvalPo, processDetailPo, rejected.getRemark(), notifyList);
        return approvalPo;
    }

    @Override
    public void sendRemindMessage(Long approvalId) {
        List<ProcessInstanceApprovePo> processInstanceApprovePoList = processInstanceApproveService.listApprovingInstances(Collections.singletonList(approvalId));
        if (CollectionUtils.isEmpty(processInstanceApprovePoList)) {
            return;
        }
        ApprovalPo approvalPo = approvalService.findApprovalByApprovalId(approvalId);
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalPo.getApprovalTemplateId());
        TemplateConfigPo config = templateConfigService.getByTemplateId(approvalPo.getApprovalTemplateId(), approvalPo.getVersion());
        sendRemindMessage(approvalPo, templatePo, config, processInstanceApprovePoList);
    }

    private void sendRemindMessage(ApprovalPo approvalPo, ApprovalTemplatePo templatePo, TemplateConfigPo config, List<ProcessInstanceApprovePo> processInstanceApprovePoList) {
        List<ProcessDetailPo> processDetailPos = processDetailService.listUnProcessApproval(approvalPo.getApprovalId());
        if (CollectionUtils.isEmpty(processDetailPos)) {
            return;
        }
        Boolean remindNotify = config.getRemindNotify();

        List<ProcessNodeNotifyConfig> nodeNotifyConfigList = processNodeNotifyConfigService.listWithProcessId(processDetailPos.get(0).getProcessId(), ApprovalMessageTypeEnum.APPROVAL_REMIND);

        if (Boolean.FALSE.equals(remindNotify) && CollectionUtils.isEmpty(nodeNotifyConfigList)) {
            return;
        }

        for (ProcessNodeNotifyConfig processNodeNotifyConfig : nodeNotifyConfigList) {
            // 检查下是否使用默认的模板
            //if (!ApprovalMessageTypeEnum.defaultValue(processNodeNotifyConfig.getMessageKey())) {}
            // 节点有对应的参数, 并且已经发送, 父节点无需再进行发送
            MessageCenterHelper.send(processNodeNotifyConfig.getMessageKey(), templatePo, approvalPo,
                    processInstanceApprovePoList.stream().map(ProcessInstanceApprovePo::getUserId).collect(Collectors.toList()),
                    config, null);
            remindNotify = Boolean.FALSE;
        }
        if (Boolean.TRUE.equals(remindNotify)) {
            //发送默认消息模板-催办通知
            MessageCenterHelper.sendDefault(ApprovalMessageTypeEnum.APPROVAL_REMIND, templatePo, approvalPo,
                    processInstanceApprovePoList.stream().map(ProcessInstanceApprovePo::getUserId).collect(Collectors.toList()),
                    config, null);
        }
    }

    @Override
    public Page<ApprovalVo> pageApproval(ApprovalQueryDTO approvalQueryDTO) {
        Page<ApprovalVo> approvalVoPage = new Page<>();

        Page<ApprovalPo> approvalPoPage = approvalService.pageApproval(approvalQueryDTO);
        approvalVoPage.setTotal(approvalPoPage.getTotal());
        approvalVoPage.setSize(approvalVoPage.getSize());
        approvalVoPage.setCurrent(approvalPoPage.getCurrent());
        approvalVoPage.setPages(approvalPoPage.getPages());

        List<ApprovalPo> records = approvalPoPage.getRecords();
        if (CollectionUtils.isEmpty(records)) {
            approvalVoPage.setRecords(Collections.emptyList());
            return approvalVoPage;
        }
        records.forEach(e -> {
            if (!ProcessEnums.APPROVING.getCode().equals(e.getStatus())) {
                e.setUserName(null);
            }
        });
        // 查找多人审批的数据
        List<Long> multiple = records.stream().filter(e -> e.getUserId().split(",").length > 1 && !StringUtils.isEmpty(e.getUserName()))
                .map(ApprovalPo::getApprovalId)
                .collect(Collectors.toList());
        //查自动发送待办
        List<Long> versionList = records.stream().map(ApprovalPo::getVersion).filter(Objects::nonNull).distinct().collect(Collectors.toList());
        Map<String, Boolean> autoNotifyMap = CommonUtil.defaultList(templateConfigService.list(Wrappers.lambdaQuery(TemplateConfigPo.class)
                .eq(TemplateConfigPo::getTemplateId, approvalQueryDTO.getApprovalTemplateId()).in(TemplateConfigPo::getVersion, versionList))).stream()
                .collect(Collectors.toMap(k -> org.apache.commons.lang3.StringUtils.joinWith("_", k.getTemplateId(), k.getVersion()), v ->
                        ObjectUtils.defaultIfNull(v.getAutoNotify(), Boolean.FALSE).booleanValue()
                                && ObjectUtils.defaultIfNull(v.getRemindNotify(), Boolean.FALSE).booleanValue()));

        List<ApprovalVo> approvalList = approvalPoPage.getRecords().stream().map(e -> {
            ApprovalVo approvalVo = e.copyApprovalVo();
            String configKey = org.apache.commons.lang3.StringUtils.joinWith("_", e.getApprovalTemplateId(), e.getVersion());
            approvalVo.setAutoNotify(ObjectUtils.defaultIfNull(autoNotifyMap.get(configKey), Boolean.FALSE));
            return approvalVo;
        }).collect(Collectors.toList());

        if (!CollectionUtils.isEmpty(multiple)) {
            Map<Long, List<ProcessInstanceApprovePo>> map = processInstanceApproveService.listApprovingInstances(multiple).stream().collect(Collectors.groupingBy(ProcessInstanceApprovePo::getApprovalId));

            approvalList.forEach(e -> {
                List<ProcessInstanceApprovePo> processInstanceApprovePos = map.get(e.getApprovalId());
                if (!CollectionUtils.isEmpty(processInstanceApprovePos)) {
                    String symbol = processInstanceApprovePos.get(0).getSymbol();
                    String userName = processInstanceApprovePos.stream().map(ProcessInstanceApprovePo::getUserName).collect(Collectors.joining(symbol));
                    e.setUserName(userName);
                }
            });
        }

        approvalVoPage.setRecords(approvalList);
        return approvalVoPage;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean transferNode(ApprovalTransferNodeDTO dto) {
        UserVO loginUserVo = UserContextHolder.getUserVo();
        if(Objects.isNull(loginUserVo)) {
            throw new ApiException(ProcessResultCode.UNAUTHORIZED);
        }
        //判断转让人是否是在职用户
        SUserVO targetUserVO = privilegeClient.getUserById(dto.getReceiverId()).getObj();
        if(Objects.isNull(targetUserVO) || !NumberCst.ZERO.equals(targetUserVO.getStatus())) {
            throw new ApiException(ProcessResultCode.NOT_ACTIVE_RECEIVER);
        }
        //查询审批实例 -只有审批中的实例才允许操作
        ApprovalPo approvalPo = this.getAndCheck(dto.getApprovalId());
        if (!ProcessEnums.APPROVING.getCode().equals(approvalPo.getStatus())) {
            throw new ApiException(ProcessResultCode.NOT_APPROVING);
        }
        //查询当前审批节点审批人信息
        List<ProcessInstanceApprovePo> allProcessInstanceApprovePoList = processInstanceApproveService.getApprovalWithoutStatus(approvalPo.getApprovalId());
        if(CollectionUtils.isEmpty(allProcessInstanceApprovePoList)) {
            throw new ApiException(ProcessResultCode.NOT_APPROVING);
        }
        //筛选当前审批节点审批中的审批人信息
        List<ProcessInstanceApprovePo> processInstanceApprovePoList = allProcessInstanceApprovePoList.stream().filter(e -> Objects.equals(e.getStatus(), StatusEnums.NORMAL.getCode())).collect(Collectors.toList());
        if(CollectionUtils.isEmpty(processInstanceApprovePoList)) {
            throw new ApiException(ProcessResultCode.NOT_APPROVING);
        }
        Map<Long, List<ProcessInstanceApprovePo>> processInstanceApprovePoMap = processInstanceApprovePoList.stream().collect(Collectors.groupingBy(ProcessInstanceApprovePo::getUserId));
        if(!processInstanceApprovePoMap.containsKey(loginUserVo.getUserId())) {
            throw new ApiException(ProcessResultCode.NOT_PRIVILEGE_TRANSFER);
        }
        Long approvalDetailId = processInstanceApprovePoList.get(0).getApprovalDetailId();
        //转让人是否在当前审批节点审批人里
        boolean existedCurrentApprover = allProcessInstanceApprovePoList.stream().anyMatch(e ->
                Objects.equals(approvalDetailId, e.getApprovalDetailId()) && Objects.equals(e.getUserId(), dto.getReceiverId()));
        if(existedCurrentApprover) {
            throw new ApiException(ProcessResultCode.EXISTED_CURRENT_APPROVER);
        }
        //查询待办信息
        List<NotifyVo> sourceNotifyList = notifyBiz.listNotifyByApprovalId(Collections.singletonList(approvalPo.getApprovalId()));
        //转让审批实例节点数据- 审批实例详情、快照不做转让
        List<ProcessInstanceApprovePo> transferProcessList = processInstanceApprovePoMap.get(loginUserVo.getUserId());
        for(ProcessInstanceApprovePo processInstanceApprovePo : transferProcessList) {
            ProcessInstanceApprovePo updateEntity = new ProcessInstanceApprovePo();
            updateEntity.setId(processInstanceApprovePo.getId());
            updateEntity.setUserId(targetUserVO.getUserId());
            updateEntity.setUserName(targetUserVO.getUsername());
            updateEntity.setUpdateBy(loginUserVo.getUserId());
            updateEntity.setUpdateName(loginUserVo.getRealName());
            updateEntity.setUpdateTime(new Date());
            String transferRemark = String.format("原审批人%s于%s转让至%s", processInstanceApprovePo.getUserName(),
                    DateUtil.format(updateEntity.getUpdateTime(), "yyyy-MM-dd HH:mm:ss"), updateEntity.getUserName());
            updateEntity.setTransferRemark(org.apache.commons.lang3.StringUtils.isBlank(processInstanceApprovePo.getTransferRemark()) ?
                    transferRemark : processInstanceApprovePo.getTransferRemark()+ "；</br>"+ transferRemark);
            processInstanceApproveService.updateById(updateEntity);
            processInstanceApprovePo.setUserId(targetUserVO.getUserId());
            processInstanceApprovePo.setUserName(targetUserVO.getUsername());
        }
        approvalPo.setUserId(processInstanceApprovePoList.stream().map(e -> e.getUserId().toString()).collect(Collectors.joining(",")));
        approvalPo.setUserName(processInstanceApprovePoList.stream().map(ProcessInstanceApprovePo::getUserName).collect(Collectors.joining(",")));
        approvalService.updateById(approvalPo);
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalPo.getApprovalTemplateId());
        //加转让日志
        ProcessTransferLogPo transferLog = new ProcessTransferLogPo();
        transferLog.setId(UuidUtil.generateId());
        transferLog.setOperatedBy(loginUserVo.getUserId());
        transferLog.setOperatedName(loginUserVo.getRealName());
        transferLog.setSourceUserId(loginUserVo.getUserId());
        transferLog.setSourceUserName(loginUserVo.getRealName());
        transferLog.setTargetUserId(targetUserVO.getUserId());
        transferLog.setTargetUserName(targetUserVO.getUsername());
        transferLog.setBusinessTemplate(JSONObject.toJSONString(Arrays.asList(templatePo.getTitle())));
        transferLog.setDataCount(1L);
        transferLog.setStatus(TransferEnum.SUCCESS.getCode());
        transferLog.setTransferType(NumberCst.THREE);
        processTransferLogService.save(transferLog);

        TransferLogDetailPo transferLogDetailPo = TransferLogDetailPo.ofDefault(approvalPo, templatePo);
        transferLogDetailPo.setTransferId(transferLog.getId());
        transferLogDetailService.save(transferLogDetailPo);


        //原审批人对应的待办事项的待办状态变为「已办」
        if(ObjectUtil.isNotEmpty(sourceNotifyList)){
            List<NotifyVo> newSourceNotifyList = sourceNotifyList.stream().filter(e -> Objects.equals(loginUserVo.getUserId(), e.getUserId())).collect(Collectors.toList());
            TransactionUtils.afterCommit(() -> notifyBiz.dealWithNotify(newSourceNotifyList, null));
        }
        //新审批人增加待办事项，待办状态为「待办」
        TemplateConfigPo config = templateConfigService.getByTemplateId(approvalPo.getApprovalTemplateId(), approvalPo.getVersion());
        List<NotifyDTO> newNotifyList = NotifyDomainService.notifyDTO(approvalPo, config, templatePo, transferProcessList);
        NotifyEventContext.addEvent(newNotifyList);
        NotifyEvent notifyEvent = NotifyEventContext.getEvent();
        if (Objects.nonNull(notifyEvent) && !CollectionUtils.isEmpty(notifyEvent.getNotifyList())) {
            TransactionUtils.afterCommit(() -> publisher.publishEvent(notifyEvent));
        }
        NotifyEventContext.clear();
        Boolean transferNotify = config.getTransferNotify();
        List<ProcessNodeNotifyConfig> nodeNotifyConfigList = null;
        //是否自定义模板
        List<ProcessDetailPo> processDetailPos = processDetailService.listUnProcessApproval(approvalPo.getApprovalId());
        if(!CollectionUtils.isEmpty(processDetailPos)) {
            Long processId = null;
            ProcessDetailPo processDetailPo = processDetailPos.get(0);
            if (processDetailPo.isSpecialNode()) {
                // 根据groupId查询对应的值
                List<ProcessRelatedPo> processRelatedPos = processRelatedService.listWithGroup(approvalPo.getApprovalTemplateId(), approvalPo.getVersion(), processDetailPo.getGroupId());
                processId = processRelatedPos.get(0).getProcessAfterId();
            } else {
                processId = processDetailPo.getNextProcessId();
            }
            nodeNotifyConfigList = processNodeNotifyConfigService.listWithProcessId(processId, ApprovalMessageTypeEnum.APPROVAL_TRANSFER);
        }
        if (Boolean.FALSE.equals(transferNotify) && CollectionUtils.isEmpty(nodeNotifyConfigList)) {
           // 不处理
        }  else {
            if(!CollectionUtils.isEmpty(nodeNotifyConfigList)) {
                String messageKey =  nodeNotifyConfigList.get(0).getMessageKey();
                if(StringUtils.hasText(messageKey)) {
                    MessageCenterHelper.send(messageKey, templatePo, approvalPo, Arrays.asList(dto.getReceiverId()), config, null);
                    transferNotify = Boolean.FALSE;
                }
            }
            // 审批转让开启才发审批转让通知
            if (Boolean.TRUE.equals(transferNotify)) {
                //发送默认消息模板-审批转让通知
                MessageCenterHelper.sendDefault(ApprovalMessageTypeEnum.APPROVAL_TRANSFER, templatePo, approvalPo, Arrays.asList(dto.getReceiverId()), config, null);
            }
        }
        return true;
    }

    @Override
    public Boolean urge(Long approvalId, String bizType, Integer voucherType, Boolean isSystem) {
        UserVO loginUserVo = UserContextHolder.getUserVo();
        if(Objects.isNull(loginUserVo)) {
            throw new ApiException(ProcessResultCode.UNAUTHORIZED);
        }
        //查询审批实例 -只有审批中的实例才允许操作
        ApprovalPo approvalPo = approvalService.findApprovalByApprovalId(approvalId);
        if (Objects.isNull(approvalPo)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }
        if (!ProcessEnums.APPROVING.getCode().equals(approvalPo.getStatus())) {
            throw new ApiException(ProcessResultCode.NOT_APPROVING);
        }
        if (!isSystem && !ObjectUtils.equals(approvalPo.getCreateBy(), loginUserVo.getUserId())) {
            throw new ApiException(ProcessResultCode.NOT_SUBMITTER);
        }
        ApprovalTemplatePo templatePo = approvalTemplateService.getById(approvalPo.getApprovalTemplateId());
        if(Objects.isNull(templatePo)) {
            throw new ApiException(ProcessResultCode.TEMPLATE_NOT_EXISTS);
        }
        //判断催办是否开启
        TemplateConfigPo config = templateConfigService.getByTemplateId(approvalPo.getApprovalTemplateId(), approvalPo.getVersion());
        if(Objects.isNull(config) || !ObjectUtils.defaultIfNull(config.getRemindNotify(), Boolean.FALSE).booleanValue()) {
            throw new ApiException(ProcessResultCode.DISABLE_REMIND);
        }
        DingDTO dingDTO = new DingDTO();
        dingDTO.setApprovalId(approvalId);
        dingDTO.setBelongSystem(templatePo.getBelongSystem());
        dingDTO.setCreateName(isSystem ? "系统" : loginUserVo.getRealName());
        //根据所属应用-区分默认催办模板
        if(ApprovalMessageTypeEnum.isMcn(templatePo.getBelongSystem())) {
            dingDTO.setMsgTemplate(ApprovalMessageTypeEnum.APPROVAL_REMIND.getMcnCode());
        } else {
            dingDTO.setMsgTemplate(ApprovalMessageTypeEnum.APPROVAL_REMIND.getEopCode());
        }
        //是否自定义模板
        List<ProcessDetailPo> processDetailPos = processDetailService.listUnProcessApproval(approvalPo.getApprovalId());
        if(!CollectionUtils.isEmpty(processDetailPos)) {
            Long processId = null;
            ProcessDetailPo processDetailPo = processDetailPos.get(0);
            if (processDetailPo.isSpecialNode()) {
                // 根据groupId查询对应的值
                List<ProcessRelatedPo> processRelatedPos = processRelatedService.listWithGroup(approvalPo.getApprovalTemplateId(), approvalPo.getVersion(), processDetailPo.getGroupId());
                processId = processRelatedPos.get(0).getProcessAfterId();
            } else {
                processId = processDetailPo.getNextProcessId();
            }
            List<ProcessNodeNotifyConfig> nodeNotifyConfigList =
                    processNodeNotifyConfigService.listWithProcessId(processId, ApprovalMessageTypeEnum.APPROVAL_REMIND);
            if(!CollectionUtils.isEmpty(nodeNotifyConfigList)) {
                for(ProcessNodeNotifyConfig processNodeNotifyConfig : nodeNotifyConfigList) {
                    dingDTO.setMsgTemplate(processNodeNotifyConfig.getMessageKey());
                    break;
                }
            }
        }
        dingDTO.setBizType(bizType);
        dingDTO.setVoucherType(voucherType);
        dingDTO.setBusinessId(approvalPo.getBusinessId());
        dingDTO.setBusinessCode(approvalPo.getBusinessCode());
        dingDTO.setBusinessParam(approvalPo.getBusinessParam());
        return notifyBiz.urge(dingDTO);
    }

    private ApprovalPo getAndCheck(Long approvalId) {
        ApprovalPo approvalPo = approvalService.findApprovalByApprovalId(approvalId);

        if (Objects.isNull(approvalPo)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }
        if (ProcessEnums.finish(approvalPo.getStatus())) {
            throw new ApiException(ProcessResultCode.APPROVAL_COMPLETED);
        }
        approvalPo.validate();
        approvalPo.updateSelf();
        return approvalPo;
    }

    /**
     * 判断使用者并且获取流程ID 1. 判定process是否存在 2. 判断process是否为新建还是流程 3. 获取当前没有被处理的process审批的groupId
     * @param useId 使用者Id(可能是用户组或者单个用户）
     * @param id 流程ID或者详情的父Id
     * @return 返回检查的Process
     */
    private CheckProcess getBelong(ApprovalPo approval, Long useId, Long id, ApprovalDTO.Params param) {

        if (Objects.isNull(approval)) {
            throw new ApiException(ProcessResultCode.APPROVAL_TEMPLATE_NOT_FOUND);
        }

        Long approvalTemplateId = approval.getApprovalTemplateId();

        CheckProcess process = new CheckProcess();
        process.setId(id);
        process.setApprovalTemplateId(approvalTemplateId);
        process.setSuccess(false);
        process.setProcess(true);

        logger.info("使用者Id={} 查询详情={}情况", useId, id);

        // 作为审批查询,在详情中可能只会有审批的权限
        List<ProcessDetailPo> processDetailByAll = processDetailService.listProcessDetail(ProcessDetailPo.builder().
        // 这里的状态是否修改，取决与审批流程是否支持重复修改，请自行修改
            parentId(id).resultType(ProcessResultTypeEnums.CREATED.getIndex()).build());

        if (processDetailByAll.isEmpty()) {
            logger.info("使用者Id={} 查询父级详情={}情况", useId, id);
            processDetailByAll = processDetailService.listProcessDetail(ProcessDetailPo.builder().parentId(id).resultType(ProcessResultTypeEnums.PARENT.getIndex()).build());

        }

        if (processDetailByAll.isEmpty()) {
            logger.info("使用者Id={} 查询结束节点详情={}情况", useId, id);
            processDetailByAll = processDetailService.listProcessDetail(ProcessDetailPo.builder().parentId(id).resultType(ProcessResultTypeEnums.END.getIndex()).build());
        }
        if (!processDetailByAll.isEmpty()) {
            ProcessDetailPo processDetailPo = processDetailByAll.get(processDetailByAll.size() - 1);
            if (Boolean.TRUE.equals(param.isApprovalFlag())) {
                id = processDetailPo.getProcessId();

                // 直接取最后一个
                Long nextProcessId = processDetailPo.getNextProcessId();
                Long groupId = processDetailPo.getGroupId();

                // 如果为或签功能. 直接计算下一个对应的是哪个
                if (ProcessConstants.OR_SIGN_NODE.equals(nextProcessId)) {
                    // 通过组数据查询数据
                    List<ProcessRelatedPo> processRelatedPos = processRelatedService.listWithGroup(approval.getApprovalTemplateId(), approval.getVersion(), groupId);
                    Optional<ProcessRelatedPo> first = processRelatedPos.stream().findAny();
                    ProcessRelatedPo processRelatedPo = first.get();
                    ProcessPo processPo = processService.getById(processRelatedPo.getProcessAfterId());
                    process.setNextProcess(processPo);
                } else if (ProcessConstants.COUNTER_SIGN_NODE.equals(nextProcessId)) {
                    List<ProcessRelatedPo> processRelatedPos = processRelatedService.listWithGroup(approval.getApprovalTemplateId(), approval.getVersion(), groupId);

                    List<Long> processIdList = processRelatedPos.stream().map(ProcessRelatedPo::getProcessAfterId).collect(Collectors.toList());
                    Map<Long, String> processMap = processService.listByIds(processIdList).stream().collect(Collectors.toMap(BaseEntity::getId, ProcessPo::getAssociateGroupId));
                    Optional<ProcessRelatedPo> first = processRelatedPos.stream().filter(e -> {
                        String d = processMap.get(e.getProcessAfterId());
                        return Objects.equals(UserContextHolder.getUserVo().getUserId(), Long.valueOf(d));
                    }).findAny();
                    ProcessRelatedPo processRelatedPo = processRelatedPos.get(0);
                    if (first.isPresent()) {
                        processRelatedPo = first.get();
                    }
                    ProcessPo processPo = processService.getById(processRelatedPo.getProcessAfterId());
                    process.setNextProcess(processPo);
                }else if (Objects.nonNull(nextProcessId)) {
                    ProcessPo nextProcess = processService.getById(nextProcessId);
                    process.setNextProcess(nextProcess);
                }
            }

            logger.info("使用者Id={} 查询到 详情={}的信息", useId, id);
            process.setProcess(false);
            process.setId(processDetailPo.getId());
        }

        logger.info("使用者Id={} 开始查询 流程Id={}", useId, id);

        ProcessPo processById = processService.getById(id);

        if (processById == null) {
            logger.warn("使用者Id={} ，开始查询 流程Id={}不存在", useId, id);
            return process;
        }

        process.setCurrentProcess(processById);
        logger.info("使用者Id={},成功查询流程组Id={}", useId, id);
        return process;
    }

    private ProcessDetailPo validateRejected(ApprovalPo approval, List<ProcessDetailPo> processList) {

        if (processList.size() <= 1) {
            throw new ApiException(ProcessResultCode.REJECT_STATUS_CHANGE);
        }
        Map<String, ProcessDetailPo> nodeMap = new HashMap<>();
        for (int i = 0; i < processList.size(); i++) {
            ProcessDetailPo p = processList.get(i);
            String code = Md5Util.md5(String.format("%s:%s", p.getProcessId(), p.getNextProcessId()));
            if (!nodeMap.containsKey(code)) {
                // 使用当前ID+下一个ID确定上一个
                nodeMap.put(code, i == 0 ? null : processList.get(i - 1));
            }
        }
        ProcessDetailPo processDetailPo = processList.get(processList.size() - 1);
        // 拿到它的上一条记录
        String code = Md5Util.md5(String.format("%s:%s", processDetailPo.getProcessId(), processDetailPo.getNextProcessId()));
        processDetailPo = nodeMap.get(code);
        // 判断该节点的情况.
        if (processDetailPo == null) {
            throw new ApiException(ProcessResultCode.REJECT_STATUS_CHANGE);
        }

        Long nextProcessId = processDetailPo.getNextProcessId();
        // 如果是特殊情况, 如或签会签等.


        if (processDetailPo.isSpecialNode()) {
            return processDetailPo.cloneProcessDetailPo();
        }
        // 非特殊情况. 判断是否为部门负责人且部门负责人不为空.
        ProcessPo process = processService.getById(nextProcessId);

        List<ProcessInstanceApprovePo> processInstanceApprovePos = processInstanceApproveService.getByDetailIdWithoutStatus(approval.getApprovalId(), processDetailPo.getId());

        if(-1L == processInstanceApprovePos.get(0).getUserId().longValue()){
             throw new ApiException(ProcessResultCode.HISTORY_HAS_NO_USER);
        }

        if (process.isDepartmentHeader()) {
            // 部门负责人的话, 需要判断一下是否有部门负责人
            // 如果之前的节点为发起人. 检查是否有上级发起人
            DepartmentHeadVO departmentHead = privilegeClient.getDepartmentHead(UserDTO.builder().userId(approval.getCreateBy()).build()).getObj();

            // 无上级负责人
            if (Objects.isNull(departmentHead) || !departmentHead.hasHeadDepartment()) {
                // 报错
                // throw new ApiException(ProcessResultCode.REJECT_STATUS_CHANGE);
                processList.remove(processList.size() - 1);
                return validateRejected(approval, processList);
            }
        }
        return processDetailPo.cloneProcessDetailPo();
    }

    private void updateStatusToRejected(ApprovalPo approval, ApprovalRejectedDTO rejected) {
        List<ProcessDetailPo> processList = processDetailService.listUnProcessApproval(approval.getApprovalId());
        ProcessDetailPo processDetailPo = processList.get(0);
        processDetailPo.reject(rejected.getRemark());
        processDetailService.updateById(processDetailPo);

        List<ProcessInstanceApprovePo> instanceApprove = processInstanceApproveService.getByDetailId(approval.getApprovalId(), processDetailPo.getId());
        List<ProcessInstanceApprovePo> instanceApproves = instanceApprove.stream().map(e -> {
            e.reject(rejected.getRemark());
            return e;
        }).collect(Collectors.toList());
        processInstanceApproveService.updateBatchById(instanceApproves);
    }

    private void afterReject(ApprovalPo approvalPo, ProcessDetailPo processDetailPo, String remark, List<NotifyVo> notifyList) {
        // 3. 查询上一个节点, 插入数据
        Long detailId = processDetailPo.getId();
        processDetailPo.setId(null);
        processDetailService.save(processDetailPo);

        // 审批记录表插入数据 processDetailPo
        List<ProcessInstanceApprovePo> processInstanceApprovePos = processInstanceApproveService.getByDetailIdWithoutStatus(approvalPo.getApprovalId(), detailId);

        List<ProcessInstanceApprovePo> instances = processInstanceApprovePos.stream().map(e -> {
            ProcessInstanceApprovePo processInstanceApprovePo = e.cloneProcessInstance();
            processInstanceApprovePo.setApprovalDetailId(processDetailPo.getId());
            return processInstanceApprovePo;
        }).collect(Collectors.toList());

        processInstanceApproveService.saveBatch(instances);

        replace(approvalPo);
        approvalService.updateById(approvalPo);

        // 处理之前的待办
        notifyBiz.dealWithNotify(notifyList, null);

        // 触发待办
        eventPublisherBiz.triggerNotify(approvalPo);

        ApprovalCallBack.ApprovalCallBackBuilder builder = ApprovalCallBack.builder().approvalFlag(Boolean.FALSE);
        eventPublisherBiz.publishEvent(builder, approvalPo.getApprovalId(), Boolean.FALSE, null, approvalPo.getUserId(),
            approvalPo.getUserName(), EventTypeEnums.REJECTED.getCode(), remark,
            processDetailPo.getApprovalTemplateId(), processDetailPo.getNextProcessId());
    }

    private void permissionCheck(ApprovalPo approvalPo) {

        // 校验后继节点的权限跟当前的操作人是否一致
        // 查询当前审批人
        List<ProcessInstanceApprovePo> processInstanceApprovePoList = processInstanceApproveService.listApprovingInstances(Collections.singletonList(approvalPo.getApprovalId()));

        boolean permissionCheck = false;
        for (ProcessInstanceApprovePo processInstanceApprovePo : processInstanceApprovePoList) {
            if (UserContextHolder.getUserVo().getUserId().equals(processInstanceApprovePo.getUserId())) {
                permissionCheck = Boolean.TRUE;
                break;
            }
        }
        if (!permissionCheck) {
            throw new ApiException(ProcessResultCode.PRIVILEGE_NOT_FOUND);
        }
    }

    private void checkDuplicated(ApprovalTemplatePo templatePo, ApprovalDTO approval) {
        // 查询模板id+业务id+业务code是否有重复数据
        List<ApprovalPo> approvalList = approvalService.findByTemplateIdAndBusiness(templatePo.getId(), approval.getBusinessId(), approval.getBusinessCode());
        if (!CollectionUtils.isEmpty(approvalList)) {
            throw new ApiException(ProcessResultCode.BUSINESS_HAS_ALREADY);
        }
    }
}
